/***************************************************************************************************
*Copyright(C),2010-2017,Sumscope
*FileName	:  PackedSizeString.h
*Author	:  scofined.qi
*Version	:  1.0
*Date		:  2017/08/04
*Desc		:  //用于主要说明此程序文件完成的主要功能
*Relation :
*Others	:  //其他内容说明
*Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
*History	:  //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
***************************************************************************************************/
#ifndef PACKEDSIZESTRING_H_9521CBCA_9888_43A7_AD26_91AD44CE5CDB
#define PACKEDSIZESTRING_H_9521CBCA_9888_43A7_AD26_91AD44CE5CDB

#include "PackedStringAllocator.h"
// 宏USE_STRING_ID用来控制PackedString是否启用m_id功能,启用后可以进一步减少内存
//#define USE_STRING_ID
#ifdef USE_STRING_ID
#define ZERO_ID()	m_id=0;
#else 
#define ZERO_ID()
#endif

// 模板参数
// CAGETORY	: 用于区分是哪一类,避免将不同作用的字符串混合放在一个分配器中
// CAPCITY  : 用于指明该字符串的最大长度特征等或者平均长度特征等
template<int CAGETORY, int CAPCITY>
class  PackedSizeString{
public:
	typedef PackedSizeString ThisType;
	typedef char Element;
	typedef unsigned short ushort;
	enum{ npos = -1 };
	template<class T>
	static void SwapValue(T& v1, T& v2){ T temp = v1; v1 = v2; v2 = temp; }
protected:
	int					m_size;		// 表示字符串长度
#ifdef USE_STRING_ID
	int					m_id;		// >0表示m_string已经从映射文件中加载过了
#endif
	Element*			m_string;	// 字符串内存,默认为m_null
public://constructors
	PackedSizeString() :m_string(GetNullString()), m_size(0){ ZERO_ID(); }
	PackedSizeString(const Element* str) : m_string(GetNullString()), m_size(0){
		ZERO_ID();
		asign(str, str ? strlen(str) : 0);
	}
	PackedSizeString(const Element* str, int len) : m_string(GetNullString()), m_size(0)
	{
		ZERO_ID();
		asign(str, len);
	}
	PackedSizeString(const ThisType& bs) : m_string(GetNullString()), m_size(0)
	{
		ZERO_ID();
		asign(bs.c_str(), bs.size());
	}
	~PackedSizeString(){ reset(); }

	ThisType& operator=(const Element* str){ return asign(str, str ? strlen(str) : 0); }
	ThisType& operator=(const ThisType& bs){ return asign(bs.c_str(), strlen(bs.c_str())); }
	ThisType& asign(const Element* str, int len)
	{
		if (str != c_str())
		{
			expand(str, len);
		}
		return *this;
	}
	ThisType& append(const Element* str, int len)
	{
		if (str && len > 0)
		{
			expand(c_str(), size(), str, len);
		}
		return *this;
	}
protected:
	void expand(const Element* str, int size, const Element* str2 = 0, int size2 = 0)
	{
#ifdef USE_STRING_ID
		if (m_id <= 0)
		{
			//第一次将不存储数据,直接写入到映射文件中,
			// 只有当调用c_str()才重新加载进来,此时才会占内存
			int id = SavePackedString(CAGETORY, str, size, str2, size2);
			reset();
			m_id = id;
			m_size = size + size2 + 1;
			m_string = GetNullString();
			return;
		}
#endif
		int total = size + size2 + 1;
		Element* buf = new Element[total];
		if (str && size > 0)
			memcpy(buf, str, size);
		if (str2 && size2 > 0)
			memcpy(buf + size, str2, size2);
		buf[total - 1] = 0;
		reset();
		m_string = buf;
		m_size = total - 1;
	}
	void reset()
	{
		if (m_string && m_string != GetNullString())
			delete[] m_string;
		m_string = 0;
		m_size = 0;
		ZERO_ID();
	}
	void swap(ThisType& bs){
		SwapValue(m_size, bs.m_size);
		SwapValue(m_string, bs.m_string);
	}
public://operators,const
	int compare(const Element* str, int len)const
	{
		if (!str || len <= 0)
			return size() <= 0 ? 0 : 1;	// 有值为大
		if (size() <= 0)
			return (!str || len <= 0) ? 0 : -1;

		// 字典排序
		int cmp = len < size() ? len : size();
		int ret = memcmp(m_string, str, cmp);
		if (ret == 0)
			return len == size() ? 0 : (cmp == len ? 1 : -1);
		return ret;
	}
	bool operator==(const ThisType& bs)const{ return compare(bs.m_string, bs.m_size) == 0; }
	bool operator==(const Element* str)const{ return compare(str, str ? strlen(str) : 0) == 0; }
	bool operator!=(const ThisType& bs)const{ return compare(bs.m_string, bs.m_size) != 0; }
	bool operator!=(const Element* str)const{ return compare(str, str ? strlen(str) : 0) != 0; }
public://accessors,const
	const Element* c_str()const
	{
		// 外界尝试访问此对象
		// 如果明显为空,那么无需加载
		if (m_string == GetNullString() || m_size <= 0)
			return GetNullString();
		// 如果m_id不为0,且mm_string==GetNullString(),那么需要加载
#ifdef USE_STRING_ID
		if (m_id != 0 && m_string == GetNullString())\
			LoadPackedString(CAGETORY, m_id, m_size, const_cast<Element*>(m_string));
#endif
		return m_string;
	}
	operator const Element*()const{ return c_str(); }
	int size()const{ return m_size; }
	int memory()const{ return sizeof(*this) + m_size + 1; }
	bool empty()const{ return !m_string || m_size <= 0; }
	Element at(int i)const{
		if (i < 0 || i >= size())return Element(0);
		return c_str()[i];
	}
	Element operator[](int i)const{ return at(i); }
};

#endif
